package de.koller.oceanFramework.tree;

import de.koller.oceanFramework.OceanException;
import de.koller.oceanFramework.gl.IOceanGame;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author Fabian Koller
 */
abstract public class Sea3 implements ISea3 {

	private IOceanGame game;
	
	private List<ISea3> add = new ArrayList<>();
	private List<ISea3> remove = new ArrayList<>();
	
	private List<ISea3> seas = new ArrayList<>();
	private boolean initialized = false;
	private boolean released = false;

	private ISea3 mother = null;
	private boolean dontUpdateChilds = false;
	
	private boolean ignoreChildUpdate = false;
	private boolean ignoreChildRender = false;
	
	/**
	 * 
	 */
	@Override
	final public void initalize() {
		if( initialized )
			return;
		
		this.init(); 
		
		addChilds();
		for( ISea3 s : seas )
			s.initalize();
		
		this.postChildInit();
		initialized = true;
	}
	
	/**
	 * 
	 */
	protected void postChildInit() {
		
	}

	/**
	 * 
	 */
	@Override
	final public void releasing() {
		if( released )
			throw new OceanException( "Already released! (" + this.toString() + ")" );
		
		this.release();
		
		for( ISea3 s : seas )
			s.releasing();
		
		released = true;
	}

	/**
	 * 
	 * @param elapsedTime
	 */
	@Override
	final public void rendering(int elapsedTime) {
		this.render(elapsedTime);
		
		if( !ignoreChildRender )
			for( ISea3 s : seas )
				s.rendering( elapsedTime );
		
		this.postChildRender();
	}

	/**
	 * 
	 * @param elapsedTime
	 */
	@Override
	final public void updating( int elapsedTime ) {
		this.update( elapsedTime );
		
		if( dontUpdateChilds ) {
			dontUpdateChilds = false;
			return;
		}
		
		addChilds();
		
		for( ISea3 s : remove ) {
			seas.remove( s );
			s.releasing();
		}
		remove.clear();
		
		if( !ignoreChildUpdate )
			for( ISea3 s : seas )
				s.updating( elapsedTime );
		
		this.postChildUpdate( elapsedTime );
	}
	
	synchronized private void addChilds() {
		for( ISea3 s : add ) {
			seas.add( s );
			s.initalize();
		}
		add.clear();
	}

	/**
	 * 
	 */
	abstract protected void init();
	/**
	 * 
	 * @param elapsedTime
	 */
	abstract protected void update( int elapsedTime );
	/**
	 * 
	 * @param elapsedTime
	 */
	abstract protected void render( int elapsedTime );
	/**
	 * 
	 */
	abstract protected void release();
	
	/**
	 * 
	 * @param elapsedTime
	 */
	public void postChildUpdate( int elapsedTime ) {
		
	}
	
	/**
	 * 
	 */
	public void postChildRender() {
		
	}
	
	/**
	 * 
	 * @param child
	 */
	@Override
	synchronized public void addChild( ISea3 child ) {
		child.setGame( game );
		child.setMother( this );
		if( !initialized )
			add.add( child );
		else {
			add.add( child );
			child.initalize();
		}
	}

	/**
	 * 
	 * @return
	 */
	@Override
	final public int childNumber() {
		return seas.size();
	}

	/**
	 * 
	 * @param index
	 * @return
	 */
	@Override
	final public ISea3 getChild( int index ) {
		return seas.get( index );
	}

	/**
	 * 
	 * @param child
	 * @return
	 */
	@Override
	public int getChild( ISea3 child ) {
		return seas.indexOf( child );
	}
	
	/**
	 * 
	 * @param index
	 */
	@Override
	synchronized final public void removeChild( int index ) {
		remove.add( this.getChild( index ) );
	}

	public void removeMe() {
		this.getMother().removeChild( this );
	}
	
	/**
	 * 
	 * @param child
	 */
	@Override
	public void removeChild( ISea3 child ) {
		if( child == null ) {
			System.err.println( "Warning, try to remove null! (" + this + ")" );
			return;
		}
			
		this.removeChild( this.getChild( child) );
	}
	
	/**
	 * 
	 * @return
	 */
	@Override
	final public IOceanGame getGame() {
		return game;
	}
	
	/**
	 * 
	 * @param game
	 */
	@Override
	final public void setGame( IOceanGame game ) {
		this.game = game;
	}

	/**
	 * 
	 * @return
	 */
	@Override
	public ISea3 getMother() {
		if( mother == null )
			throw new OceanException( "Mother is null. Meaning it is not initialized or root! (" + this.toString() + ")" );
		return mother;
	}

	/**
	 * 
	 * @param mother
	 */
	@Override
	public void setMother( ISea3 mother ) {
		this.mother = mother;
	}

	/**
	 * 
	 * @param mother
	 * @return
	 */
	@Override
	@Deprecated
	public ISea3 searchMother( Class<? extends ISea3> mother ) {
		if( mother.isAssignableFrom( this.getClass() ) )
			return this;
		
		try { 
			this.getMother(); 
		} catch( OceanException ex ) {
			throw new OceanException( "Kein vorkommen dieser Klasse! (" + mother.toString() + ")" );
		}
		return this.getMother().searchMother( mother );
	}

	/**
	 * 
	 */
	protected void dontUpdateChilds() {
		this.dontUpdateChilds = true;
	}
	
	/**
	 * 
	 * @return
	 */
	public boolean isInitialized() {
		return initialized;
	}

	@Override
	public int countChilds() {
		int c = 0;
		
		for( ISea3 s : seas )
			c += s.countChilds();
		
		c++;
		
		return c;
	}

	public void setIgnoreChildRender( boolean ignoreChildRender ) {
		this.ignoreChildRender = ignoreChildRender;
	}

	public void setIgnoreChildUpdate( boolean ignoreChildUpdate ) {
		this.ignoreChildUpdate = ignoreChildUpdate;
	}

	public boolean isIgnoreChildRender() {
		return ignoreChildRender;
	}

	public boolean isIgnoreChildUpdate() {
		return ignoreChildUpdate;
	}

	public boolean isReleased() {
		return released;
	}

	public List<ISea3> getSeas() {
		return seas;
	}

}
